package cn.zs.supply.common.utils;

import cn.zs.supply.common.constants.enums.PubErrCode;
import cn.zs.supply.common.core.exception.BusiException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.security.SecureRandom;
import java.util.HashSet;

@Component
@Slf4j
public class OrderNoCenter {
    // 基准时间
    private long twepoch = 1517500800000L; //2018-2-2 0:0:0
    /**
     * 节点 ID 默认取1
     */
    @Value("${global.workerId}")
    private long workerId = 1;

    /**
     * 机器标识位数
     */
    private final long workerIdBits = 10L;

    /**
     * 机器ID最大值
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits); //结果就是2的workerBits次方-1,能表示的最大数.全部1亦或10位0，就是0开头最后10位1

    /**
     * 毫秒内自增位
     */
    private final long sequenceBits = 12L;
    /**
     * 机器ID偏左移12位
     */
    private final long workerIdShift = sequenceBits;

    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    /**
     * 序列id 默认取1
     */
    private long sequence = 1;

    private long lastTimestamp = -1L;
    /**
     * 时间毫秒左移22位
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits;

    /**
     * 生成时间
     *
     * @return
     */
    private long timeGen() {
        return System.currentTimeMillis();
    }

    @PostConstruct
    public void init() {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("workerId错误，取值范围为[0-1023]", maxWorkerId));
        }
    }

    /**
     * 获取id 线程安全
     *
     * @return
     */
    public synchronized long nextNo() {
        long timestamp = timeGen();
        // 时间错误
        if (timestamp < lastTimestamp) {
//            if((lastTimestamp - timestamp) >2000){
//                throw new BusinessException("系统时间已回退");
//            }
            try {
                Thread.sleep((lastTimestamp - timestamp) + 2);
                timestamp = timeGen();
            } catch (InterruptedException e) {
                log.error("获取订单号异常", e);
                throw new BusiException(PubErrCode.COMMON_ERROR, "获取订单号异常" + e.getMessage());
            }
        }
        // 当前毫秒内，则+1
        if (lastTimestamp == timestamp) {
            // 当前毫秒内计数满了，则等待下一秒
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = new SecureRandom().nextInt(10);
        }
        lastTimestamp = timestamp;
        long id = ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
        return id;
    }

    /**
     * 等待下一个毫秒的到来
     *
     * @param lastTimestamp
     * @return
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    public static HashSet<Long> idSet = new HashSet<>();

    public static void main(String[] args) {
        OrderNoCenter orderNoCenter = new OrderNoCenter();
        Long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            long orderNo = orderNoCenter.nextNo();
            // System.out.println(orderNo);
//             if(idSet.contains(orderNo)){
//                 System.out.println("订单号重复: " + orderNo);
//             }
            idSet.add(orderNo);
        }
        Long end = System.currentTimeMillis();
        System.out.println("用时: " + (end - start) / 1000);
        System.out.println("订单号数量: " + idSet.size());

    }
}